home *** CD-ROM | disk | FTP | other *** search
- ***************************************************************************
- * Wie schreibt man externe Player? *
- ***************************************************************************
-
- © 1992 by Delirium
- Datum 12.09.1992
-
-
-
- Das externe Player Konzept
-
- DeliTracker unterstützt sog. externe Player. Das sind Player, die in einem
- speziellen Format vorliegen und von DeliTracker nachgeladen werden können.
- Ist ein solcher Player geladen worden, erkennt und spielt DeliTracker die
- Module, die von diesem Player unterstützt werden. Derzeit kann DeliTracker
- maximal 64 externe Player nachladen. Dies sollte jedoch erst mal für eine
- Weile reichen.
-
- Beim Start von DeliTracker werden alle Player, die sich im Verzeichnis
- 'DeliPlayers' (b.z.w im Configfile gespeicherten Playerpfad) befinden,
- geladen. Zusätzlich haben Sie jederzeit die Möglichkeit einen weiteren
- bzw. neueren Player nachzuladen. Diese Player verleihen DeliTracker
- seine ungeahnte Flexibilität.
-
- Externe Player sind Executables, d.h. sie werden reloziert. Am Anfang
- besitzen sie die charakteristische Playerstruktur. Sie wird mit dem Macro
- aus dem Includefile 'misc/deliplayer.i' erzeugt.
-
- Es ist relativ einfach, eine Replayroutine an DeliTracker anzupassen.
- Das 'einzige' was Sie tun müssen ist ein wenig Interfacecode zu schreiben.
- Dies ist aber halb so wild, denn DeliTracker stellt ihnen viele hilfreiche
- Routinen zur Verfügung.
-
-
- 1. Grundvoraussetzungen
-
- Um ein neues Soundsystem anzupassen, benötigen Sie zum einen den Sourcecode
- oder ein linkbares Objektfile der Replayroutine, zum anderen mindestens ca.
- 5 Module zum testen der Funktionstüchtigkeit des Players.
-
-
- 2. Schematischer Aufbau eines externen Players
-
- {
- Playerheader
-
- TagArray
-
- Interfacecode
-
- Replaycode+Daten
- }
-
-
- 3. Funktionen, die der externe Player zur Verfügung stellt
-
- Das PLAYERHEADER Macro generiert den Header, der das File als externen
- Player identifiziert. Dieses Macro muß angegeben werden und ganz am
- Anfang des Players stehen. Als einziger Parameter ist ein Pointer auf ein
- Tag Array einzutragen, in dem alle Funktionen stehen, die der Player
- DeliTracker zur Verfügung stellt. Bei allen Funktionsaufrufen außer der
- Interruptroutine (DTP_Interrupt) enthält a5 die Base. Globale Variablen
- können dadurch adressiert werden. (Siehe auch 'misc/deliplayer.i'). Da
- DeliTracker vor Aufruf einer Playerroutine (außer bei DTP_Interrupt) alle
- Register sichert, dürfen diese verändert werden.
-
-
- PLAYERHEADER <tagarray>
-
- tagarray Pointer auf ein Tag Array, das mit TAG_DONE
- abgeschlossen sein muß. Tags, bei denen ti_Data NULL
- ist, werden ignoriert.
- Außerdem können folgende SystemTags verwendet werden:
- TAG_DONE
- TAG_IGNORE
- TAG_MORE
- TAG_SKIP
-
-
- Derzeit existieren folgende Tags:
-
- DTP_CustomPlayer dieser Tag deklariert einen Player als Customplayer
- ti_Data darf nicht NULL sein !
-
- DTP_RequestDTVersion damit kann man sicherstellen, daß mindestens eine
- bestimmte Version von DeliTracker vorhanden ist.
- Dieser Tag muß angegeben werden, wenn bei den
- DeliTrackerGlobals neue Funktionspointer hinzugekommen
- sind und diese vom Player benutzt werden. ti_Data ist
- dabei die Playerrevision.
-
- DTP_RequestV37 wenn dieser Tag vorhanden ist, wird der Player nur
- noch von der Kick 2.0 Version von DeliTracker geladen.
- (d.h. dtg_GadToolsBase ist gültig)
-
- DTP_PlayerVersion Tag, der die Revisionsnummer des Players enthält.
- Bei zwei Playern mit dem gleichen Playernamen wird
- derjenige mit der größeren Revisionsnummer geladen.
-
- DTP_PlayerName ti_Data enthält den Pointer auf den Namen des Players.
- Dieser String kann zwar beliebig lang sein aber zur
- Zeit werden nur die ersten 24 Zeichen angezeigt.
- Dieser Tag muß existieren !
-
- DTP_Creator Pointer auf den Namen des Autors. Dieser wird im Setup-
- fenster im Playerinfofeld angezeigt. Dieser String kann
- $A als Zeilenumbruch enthalten.
-
- DTP_Check1 Pointer auf eine Modulerkennungsroutine, die aufgerufen
- wird, wenn 1024 Bytes des Moduls geladen sind. Wird das
- Modul erkannt, liefert sie d0=0, ansonsten d0<>0.
-
- DTP_Check2 Pointer auf eine Modulerkennungsroutine, die aufgerufen
- wird, wenn das komplette Modul geladen und evtl. entpackt
- ist. Wird das Modul erkannt, liefert sie d0=0, ansonsten
- d0<>0.
-
- DTP_ExtLoad Pointer auf eine optionale Laderoutine für Module. Ist
- kein Fehler aufgetreten, wird d0=0 zurückgegeben, sonst
- d0<>0. Hinweis: Achten Sie darauf, daß im Fehlerfall alle
- allocierten Ressourcen (Memory, Locks, ...) wieder frei-
- gegeben werden, da dann keine weiteren Playerfunktionen
- mehr angesprungen werden.
-
- DTP_Interrupt Pointer auf eine Interruptroutine, die mittels eines
- Timerinterrupts standardmäßig alle 1/50 sec aufgerufen
- wird. Dies ist die Standardmethode, um mit der richtigen
- Abspielgeschwindigkeit auf im PAL/NTSC/Productivity
- Videomodus zu spielen. Wenn keine DTP_faster/DTP_slower
- Tags angegeben sind, übernimmt DeliTracker dies durch
- Verändern der Interruptfrequenz. Dieser Tag kann auch
- nicht existieren, dann müssen aber DTP_StartInt/DTP_StopInt
- vorhanden sein !
-
- DTP_Stop Pointer auf eine optionale Stoproutine. Wenn dieser Tag
- nicht vorhanden ist, verfährt DeliTracker folgendermaßen:
- Interrupt stoppen (DTP_StopInt)
- Sound Cleanup (DTP_EndSnd)
- Song initialisieren (DTP_InitSnd)
- Ansonsten hat diese Routine die Aufgabe, einen evtl.
- spielenden Song anzuhalten und so zu initialisieren, daß
- dieser beim nächsten Starten des Interrupts von Anfang an
- zu spielen beginnt.
-
- DTP_Config Pointer auf eine optionale Initialisierungsroutine. Diese
- wird nur einmal unmittelbar nach dem Laden des Players
- aufgerufen. Mögliche Anwendungen: Laden einer player-
- spezifischen Konfigurationsdatei.
-
- DTP_UserConfig Pointer auf eine optionale Initialisierungsroutine. Diese
- wird nur dann aufgerufen, wenn der User den Player im
- Setupfenster anwählt und das 'Config' Gadget drückt.
- Mögliche Anwendungen: Öffnen eines Fensters zum Setzen
- weiterer Optionen (Instrumentenpfad, ...) und abspeichern
- einer playerspezifischen Konfigurationsdatei.
-
- DTP_SubSongRange Dieser Tag sollte angegeben werden, wenn der Player Multi-
- Module unterstützt. ti_Data zeigt dabei auf eine Routine,
- die als Returnwert in d0 die minimale und in d1 die maximale
- Subsongnummer zurückgeben muß.
-
- DTP_InitPlayer Pointer auf eine optionale Initialsierungsroutine, die
- aufgerufen wird, wenn ein Modul erfolgreich geladen wurde.
- Tritt kein Fehler auf, liefert sie d0=0, ansonsten d0<>0.
- Hier muß die Allocation der Audiokanäle stattfinden !
- (DeliTracker stellt dafür eine eigene Routine zur Verfügung)
- Falls der Player Multi-Module unterstützt, muß hier
- dtg_SndNum(a5) auf die erste Subsongnummer gesetzt werden.
- Falls eine Routine für DTP_SubSongRange existiert, macht
- DeliTracker das automatisch (d.h. die Initialisierung von
- dtg_SndNum(a5) kann weggelassen werden).
-
- DTP_EndPlayer Pointer auf eine optionale Cleanuproutine, die aufgerufen
- wird, wenn das Modul aus dem Speicher entfernt wird. Hier
- muß die Freigabe der Audiokanäle stattfinden ! (DeliTracker
- stellt dafür eine eigene Routine zur Verfügung)
-
- DTP_InitSound Pointer auf eine optionale Initialsierungsroutine.
- Diese muß das Modul initialisieren, so daß beim Starten
- des Interrupts der Song von Anfang an zu spielen beginnt.
-
- DTP_EndSound Pointer auf eine optionale Cleanuproutine.
- Diese kann z.B. die Lautstärkeregister und die Audio-DMA
- rücksetzen.
-
- DTP_StartInt Pointer auf eine Initialsierungsroutine, die existieren
- muß, wenn DTP_Interrupt nicht vorhanden ist. In diesem
- Fall muß hier der Sound gestartet werden.
-
- DTP_StopInt Pointer auf eine Cleanuproutine, die existieren muß,
- wenn DTP_Interrupt nicht vorhanden ist. In diesem Fall
- muß hier der Sound gestoppt werden.
-
- DTP_Volume Pointer auf eine Funktion, die die Lautstärke neu setzt.
- Die Funktion wird aufgerufen, wenn die Volume neu gesetzt
- wird (Slider, ARexx-Kommando) und beim Initialisieren des
- Moduls vor DTP_InitSnd. Die Mastervolume steht in
- dtg_SndVol(a5). Die Mastervolume ist dabei der maximale
- Volumewert. Die effektive Volume errechnet sich also
- durch: VOL_eff = ( ( dtg_Volume(a5) * modulevolume ) >> 6 )
- Näheres siehe Beispielsourcen.
-
- DTP_Balance Pointer auf eine Funktion, die die Balance neu setzt.
- Die Funktion wird aufgerufen, wenn die Balance neu gesetzt
- wird (Slider, ARexx-Kommando) und beim Initialisieren des
- Moduls vor DTP_InitSnd. Die Balance für die linken Kanäle
- steht in dtg_SndLBal(a5), für die rechten Kanäle in
- dtg_SndRBal(a5). Hinweis: Alle Player die Balance unterstützen
- können auch Volume ! Man verwendet dann die gleiche Routine
- zum setzen der Volume&Balance. Die linke Volume errechnet
- sich wie folgt: ( ( dtg_Volume(a5) * dtg_SndLBal(a5) ) >> 6 )
- Entsprechendes gilt für rechts.
-
- DTP_Faster Pointer auf eine Funktion, die den Abspielvorgang
- beschleunigt.
-
- DTP_Slower Pointer auf eine Funktion, die den Abspielvorgang
- verlangsamt.
-
- DTP_NextPatt Pointer auf eine Funktion, die den Patternzeiger
- um eins erhöht.
-
- DTP_PrevPatt Pointer auf eine Funktion, die den Patternzeiger
- um eins erniedrigt.
-
- DTP_NextSong Pointer auf eine Funktion, die Subsongnummer auf den
- nächsten Subsong setzt und diesen spielt.
- (Falls vorhanden)
-
- DTP_PrevSong Pointer auf eine Funktion, die Subsongnummer auf den
- vorhergehenden Subsong setzt und diesen spielt.
- (Falls vorhanden)
-
-
-
- 4. Funktionen, die DeliTracker dem externen Player zur Verfügung stellt
-
- Eine Funktion wird wie folgt aufgerufen:
-
- move.l dtg_XXX(a5),a0
- jsr (a0)
-
- Alle folgenden Funktionen außer dtg_SongEnd verwenden d0/d1/a0/a1 als
- Scratchregister. In a5 muß bei allen Aufrufen (außer dtg_SongEnd) die
- Base stehen.
-
- Derzeit existieren folgende Funktionen:
-
- dtg_GetListData
-
- SYNOPSIS
- memory size = dtg_GetListData(number)
- a0 d0 d0.l
-
- FUNCTION
- Liefert Adresse und Länge eines mit LoadFile() geladenen Files.
-
- INPUTS
- number - Nummer eines Files beginnend mit 0 für das vom User
- selektierte File.
-
- RESULT
- memory - ein Pointer auf die Startadresse des Files im Speicher
- oder NULL im Fehlerfall.
- size - Länge des Files in Bytes bzw. 0 im Fehlerfall.
-
-
- dtg_LoadFile
-
- SYNOPSIS
- success = dtg_LoadFile(name)
-
- FUNCTION
- Lädt und entpackt ggf. das angegebene File ins Chip-Memory.
- (Hinweis: diese Funktion ergänzt automatisch, falls das File
- mit dem angegebenen Namen nicht geöffnet werden konnte '.pp')
-
- INPUTS
- name - der Filename steht in einem internen Buffer (seine
- Adresse steht in dtg_PathArray)
-
- RESULT
- success - alles ok d0.l=0, sonst d0.l<>0.
-
-
- dtg_CopyDir
-
- SYNOPSIS
- dtg_CopyDir()
-
- FUNCTION
- Kopiert das Directory des von User angewählten Files an das
- Ende des Strings, auf den dtg_PathArray(a5) zeigt.
-
-
- dtg_CopyFile
-
- SYNOPSIS
- dtg_CopyFile()
-
- FUNCTION
- Kopiert den Filenamen des von User angewählten Files an das
- Ende des Strings, auf den dtg_PathArray(a5) zeigt.
-
-
- dtg_CopyString
-
- SYNOPSIS
- dtg_CopyString(string)
- a0
-
- FUNCTION
- Kopiert den String, auf den das Register a0 zeigt, an das
- Ende des Strings, auf den dtg_PathArray(a5) zeigt.
-
- INPUTS
- string - der Pointer auf den anzuhängenden String steht in a0
-
-
- dtg_AudioAlloc
-
- SYNOPSIS
- success = dtg_AudioAlloc()
-
- FUNCTION
- Belegt alle Audiokanäle.
-
- RESULT
- success - alles ok d0.l=0, sonst d0.l<>0.
-
-
- dtg_AudioFree
-
- SYNOPSIS
- dtg_AudioFree()
-
- FUNCTION
- Gibt die mit dtg_AudioAlloc belegten Audiokanäle wieder frei.
-
-
- dtg_StartInt
-
- SYNOPSIS
- dtg_StartInt()
-
- FUNCTION
- Startet den Soundinterrupt. (Falls er nicht schon läuft.)
- Falls DTP_Interrupt existiert, startet DeliTracker einen
- Timerinterrupt, ansonsten wird DTP_StartInt aufgerufen.
-
-
- dtg_StopInt
-
- SYNOPSIS
- dtg_StopInt()
-
- FUNCTION
- Stoppt den Soundinterrupt. (Falls er nicht schon angehalten
- ist.) Falls DTP_Interrupt existiert, stoppt DeliTracker seinen
- Timerinterrupt, ansonsten wird DTP_StopInt aufgerufen.
-
-
- dtg_SongEnd
-
- SYNOPSIS
- dtg_SongEnd()
-
- FUNCTION
- Signalisiert DeliTracker, daß das Modul einmal komplett
- gespielt wurde. Diese Funktion verändert keine Register
- und darf auch von Interrupts aufgerufen werden.
-
-
- dtg_CutSuffix
-
- SYNOPSIS
- dtg_CutSuffix()
-
- FUNCTION
- Entfernt am Ende des Strings, auf den dtg_PathArray(a5) zeigt
- ggf. die Endung '.pp'
-
-
-
- 5. Die Modul-Erkennung
-
- Damit DeliTracker die einzelnen Module unterscheiden kann, befindet sich in
- jedem Player eine Erkennungsroutine, die auf ein zugehöriges Modul testet.
- Diese Routine prüft auf Stellen im Modul, die bei allen Modulen eines Players
- gleich sind. So z.B. auf 'M.K.' bei Offset $438 im NoiseTracker-Player.
- Bei Sound- und NoiseTracker ist nur das Modul abgespeichert, z.B. bei MarkII
- hängt vor jedem Modul noch die Replayroutine. Bei so einem Modul müssen Sie
- auf signifikante Assemblerbefehle testen. Ein Vergleich auf ein oder zwei
- Sprungbefehle, egal ob bra oder jmp, ist hier nicht ausreichend, da bei
- vielen Modulen dieses Typs Sprungtabellen am Anfang stehen und der Player
- möglicherweise das falsche Modul erkennen könnte, was in den meisten Fällen
- zum Absturz führt.
- Der Player MUß GENAU EINE Checkroutine verwenden. Daraus ergeben sich zwei
- Grundtypen von Playern.
-
-
- 1. Typ eins Player - in der Regel etwas komplexer
- Bei ihnen ist die Check1 Funktion implementiert.
-
- Vorteil: Es lassen sich auch Player realisieren, die das Modul selbst nachladen.
- Nachteil: Gepackte Files werden nicht unterstützt.
-
- Dieser Player sollte nur für Härtefälle verwendet werden.
- (z.B: FTM, IFF-8SVX Player, der das Sample während dem Spielen nachlädt, ...)
-
-
- 2. Typ zwei Player - die einfachere Variante
- Bei ihnen ist die Check2 Funktion implementiert
-
- Vorteil: Das Modul kann gepackt sein, der Player bemerkt davon nichts.
- Nachteil: Das Modul ist in jedem Fall im Chip-Memory.
-
- Es sollte im Normalfall dieser Playertyp verwendet werden.
-
-
- Egal ob Typ eins oder zwei, wenn der Player das Modul erkannt hat, muß er
- in d0.l=0 zurückliefern, wenn nicht d0.l<>0.
-
-
- 6. Player-Interrupt
-
- Hier gibt es auch grob eine Einteilung in zwei verschiedene Typen.
-
- 1. Player die den DeliTracker Interrupt verwenden
- Vorteil: Der Player ist vom Videomodus unabhängig.
- Besitzt automatisch die faster/slower Funktion.
- Kein Aufwand für den Interrupt (Code + Interruptstruktur).
- Ist kompatibel zum serial.device
- Nachteil: Der Interrupt kommt nicht synchron zum VBlank. (Dies führt
- nur in den seltensten Fällen zu Problemen.)
-
- 2. Player die ihren eigenen Interrupt erzeugen.
- Vorteil: Es können andere Interruptquellen benutzt werden
- (z.B. AudioIRQ).
- Nachteil: Erhöhter Aufwand, bei VBlank nicht mehr unabhängig vom
- Videomodus.
-
- Wenn ein eigener Timerinterrupt verwendet wird, sollte die CIAB
- (wg. OS2.0) und die entsprechenden Resourcefunktionen verwendet werden.
- Außerdem ist es sehr sinnvoll die Replayroutine nicht direkt im CIA-B Timer-
- Interrupt abspielen zu lassen, sondern im Timerinterrupt einen Soft Interrupt
- mittels Cause() zu generieren. In diesem SoftInt kann man dann die eigentliche
- Replayroutine ablaufen lassen. So hat man den Vorteil, daß man wesentlich
- kompatibler zum serial.device ist. Dies liegt daran, daß SoftInt eine
- niedrigere Priorität als der der RBF (Read Buffer Full) Interrupt hat d.h.
- erst wird der serielle Port bedient, dann erst die Replayroutine.
- Es wird davor gewarnt, direkt in die Interruptvektoren zu schreiben!
- Zur Erinnerung: vom Betriebssystem werden die Funktionen AddIntServer()
- und SetIntVector() zur Verfügung gestellt !
-
-
- 7. Custom Module
-
- Dies sind keine Module im herkömmlichen Sinn, sondern im wesentlichen
- Player, die ein Spezialmodul beinhalten. Mit diesem Format können Sie
- auch die ausgefallensten Module mit DeliTracker abspielen. Sollten für
- den Player jedoch mehrere Module existieren, ist es in dem Fall ratsam,
- einen richtigen Player zu schreiben.
-
- Schematischer Aufbau eines Custom Moduls
-
- {
- Playerheader
-
- TagArray
-
- Interfacecode
-
- Replaycode+Daten
-
- MODUL
- }
-
- Mit dem Tag DTP_CustomPlayer wird ein Player als Customplayer deklariert,
- folgende Tags sind dann bedeutungslos:
-
- - DTP_PlayerVersion
- - DTP_PlayerName
- - DTP_Creator
- - DTP_Check1
- - DTP_Check2
- - DTP_ExtLoad
- - DTP_Config
- - DTP_UserConfig
-
-
- 8. Checkliste
-
- Dies ist eine kleine Checkliste für Player/Custommodul Anpassung, die erfüllt
- werden muß, damit der Player systemkonform ist und einwandfrei funktioniert.
-
- [ ] ist die Checkroutine präzise genug ?
- [ ] werden die Audiokanäle richtig belegt und freigegeben ?
- [ ] wird aller allocierter Speicher freigegeben ?
- [ ] werden alle Locks wieder freigegeben ?
- [ ] enforcer und mungwall - Test bestanden ?
- [ ] werden alle möglichen Fehler korrekt abgefangen ?
- [ ] wurde der Player mit 1.2, 1.3 und 2.0 getestet ?
- [ ] spielt der Player in allen Videomodi korrekt ?
-
-
- 9. Diverses
-
- Der Player sollte den Zustand der LED unbeeinflußt lassen, da im Setupwindow
- ein entsprechende Funktion existiert.
-
-
- Problemecke
-
- Symptom mögliche Ursache Beseitigung
-
- Absturz a5 überschrieben
- Register nicht gesichert
- falsche oder zu späte Initialisierung
- Modul zu neu für den Player präziserer Check
-
- klingt schräg Audiodaten nicht im Chipmem Player ins Chipmem
- falsche Initialisierung
- Modul zu neu für den Player präziserer Check
- Player benötigt bestimmte Werte in zusätzliche Initialisierung
- einigen Registern
- unverträglicher Videomodus
-
- kein Ton Audio-DMA abgeschaltet
- bei >68000 Player schreibt direkt auf Betriebssystemfuntionen
- Interruptvektoren benutzen
-
- fast keine fehlerhaftes Interrupt-Handling bei VBlank-IRQ:
- freie CPU-Zeit Z-Flag muß am Ende gesetzt sein!
-
- lahmes System 68000/020/030 zu langsam 68040 kaufen ;-)
-
-
-
-
-
-